#include "./../../../include/online/Reactor/EventLoop.h"
#include "./../../../include/online/Reactor/Acceptor.h"
#include "./../../../include/online/Reactor/TcpConnection.h"
#include <unistd.h>
#include <iostream>
#include <sys/eventfd.h>

using namespace std;

EventLoop::EventLoop(Acceptor &acceptor)
: _epfd(createEpollFd()) // 创建epoll文件描述符
,_evtList(1024) // 初始化事件列表，大小为1024
,_isLooping(false) // 初始化循环标志为false
,_acceptor(acceptor) // 传入Acceptor对象
,_conns() 
,_evtfd(createEventFd())
,_mutex()
{
    // 将监听的listenfd文件描述符添加到epoll中进行监听
    int listenfd = _acceptor.fd();
    addEpollReadFd(listenfd);

    // 将用于通信的文件描述符_evtfd放在红黑树上进行监听
    addEpollReadFd(_evtfd);
}

EventLoop::~EventLoop()
{
    // 在析构时关闭epoll文件描述符
    close(_epfd);

    // 关闭用于通信的文件描述符
    close(_evtfd);
}

// 启动事件循环
void EventLoop::loop()
{
    _isLooping = true; // 设置循环标志为true
    while (_isLooping) // 当循环标志为true时，持续进行事件处理
    {
        waitEpollFd(); // 等待epoll事件
    }
}

// 停止事件循环
void EventLoop::unloop()
{
    _isLooping = false; // 设置循环标志为false，退出循环
}

// 封装epoll_wait函数，等待epoll事件发生
void EventLoop::waitEpollFd()
{
    // 获取vector的第一个元素的首地址
    // do-while语句，保证至少执行一次循环体
    int nready = 0;
    do
    {
        // 等待epoll事件，最多阻塞5秒
        nready = ::epoll_wait(_epfd, &_evtList[0], _evtList.size(), 5000); 
    } while ((nready == -1 && errno == EINTR)); // 如果被中断，继续等待

    // 错误处理
    if (nready == -1)
    {
        cerr << "nready == -1" << endl; // 输出错误信息
        return;
    }
    else if (nready == 0)
    {
        cout << ">>epoll_wait timeout!!!" << endl; // 如果超时没有事件发生
    }
    else
    {
        // 如果事件列表已满，则扩容
        if (nready == (int)_evtList.size())
        {
            _evtList.resize(2 * nready); // 扩容为2倍
        }

        // 处理发生的事件
        for (int index = 0; index < nready; ++index)
        {
            int fd = _evtList[index].data.fd; // 获取事件对应的文件描述符
            int listenfd = _acceptor.fd();    // 获取监听文件描述符
            if (fd == listenfd)
            {
                // 如果是监听文件描述符，处理新的连接请求
                handleNewConnection();
            }
            else if(fd == _evtfd)  // 用于通信的文件描述符_evtfd就绪了
            {
                handleRead();  // 将内核计数器上的值清空
                // 需要做“任务”，此处的任务是指经过线程池的线程
                // 处理完业务逻辑之后，要发给客户端的数据
                
                // 遍历vector
                doPendingFunctors();
            }
            else
            {
                // 如果是已有连接的文件描述符，处理旧连接上的消息
                handleMessage(fd);
            }
        }
    }
}

// 处理新的连接
void EventLoop::handleNewConnection()
{
    // 通过accept获取新的连接的文件描述符
    int connfd = _acceptor.accept();
    if (connfd < 0)
    {
        perror("handleNewConnection"); // 如果accept失败，输出错误信息
        return;
    }

    // 走到这，表明三次握手已经成功了，将新的连接文件描述符connfd添加到epoll中进行监听
    addEpollReadFd(connfd);

    // 创建TcpConnection对象
    TcpConnectionPtr con(new TcpConnection(connfd,this));

    // 就可以将这三个数据成员（回调函数）传递给TcpConnection连接的对象
    con.get()->setNewConnectionCallback(_onNewConnection); // 连接建立
    con.get()->setMessageCallback(_onMessage);             // 消息到达
    con.get()->setCloseCallback(_onClose);                 // 连接断开

    // 将连接的文件描述符connfd和此连接TcpConnection对象存入容器map中
    _conns[connfd] = con;

    // 走到这，说明三次握手成功了，就可以执行连接建立的回调函数了
    con.get()->handleNewConnectionCallback();
}

// 处理已有连接上的消息
void EventLoop::handleMessage(int fd)
{
    // 查找此文件描述符fd对应的TcpConnection连接对象
    auto it = _conns.find(fd);
    if (it != _conns.end()) // 如果连接存在
    {
        // 判断连接是否断开了
        bool flag = it->second.get()->isClose();
        if (flag) // 连接断开了
        {
            it->second.get()->handleCloseCallback(); // 处理连接断开的事件

            // 将文件描述符从红黑树上取消监听
            delEpollReadFd(fd);
            // 将文件描述符以及对应的TcpConnection连接从map中删除
            _conns.erase(it);
            return;
        }
        else
        {
            // 处理消息（文件描述符可读）
            it->second.get()->handleMessageCallback();
            return;
        }
    }
    else
    {
        cout << "该连接是不存在的" << endl; // 如果连接不存在，输出错误信息
        return;
    }
}

// 封装epoll_create函数，创建epoll文件描述符
int EventLoop::createEpollFd()
{
    int epfd = ::epoll_create(1); // 创建epoll实例
    if (epfd < 0)
    {
        perror("createEpollFd"); // 如果创建失败，输出错误信息
        return -1;
    }
    return epfd;
}

// 将文件描述符添加到epoll实例中进行监听
void EventLoop::addEpollReadFd(int fd)
{
    struct epoll_event evt;
    evt.events = EPOLLIN;                                  // 监听读事件
    evt.data.fd = fd;                                      // 设置文件描述符
    int ret = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, fd, &evt); // 添加到epoll中
    if (ret < 0)
    {
        perror("addEpollReadFd"); // 如果添加失败，输出错误信息
        return;
    }
}

// 从epoll实例中删除文件描述符的监听
void EventLoop::delEpollReadFd(int fd)
{
    struct epoll_event evt;
    evt.events = EPOLLIN;                                  // 监听读事件
    evt.data.fd = fd;                                      // 设置文件描述符
    int ret = ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, &evt); // 从epoll中删除监听
    if (ret < 0)
    {
        perror("delEpollReadFd"); // 如果删除失败，输出错误信息
        return;
    }
}

// 三个回调的注册
// 连接的建立
void EventLoop::setNewConnectionCallback(TcpConnectionCallback &&cb)
{
    _onNewConnection = std::move(cb); // 补充：因为setNewConnectionCallback函数接收的参数是一个右值，而形参cb本身是一个左值
}

// 消息到达
void EventLoop::setMessageCallback(TcpConnectionCallback &&cb)
{
    _onMessage = std::move(cb);
}

// 连接的断开
void EventLoop::setCloseCallback(TcpConnectionCallback &&cb)
{
    _onClose = std::move(cb);
}

// 创建用于通信的文件描述符
int EventLoop::createEventFd()
{
    int evtfd = eventfd(0, 0);
    if (evtfd < 0)
    {
        perror("createEventFd");
        return -1;
    }
    return evtfd;
}

// 封装read,读取内核计数器
void EventLoop::handleRead()
{
    uint64_t one = 1;
    ssize_t s = read(_evtfd, &one, sizeof(uint64_t));
    if (s != sizeof(uint64_t))
    {
        perror("handleRead");
        return;
    }
}

// 封装write,往内核计数器中写，用于唤醒
void EventLoop::wakeup()
{
    uint64_t one = 1;
    ssize_t s = write(_evtfd, &one, sizeof(uint64_t));
    if (s != sizeof(uint64_t))
    {
        perror("wakeup");
        return;
    }
}

// 遍历vector<Functors>，执行“任务”
void EventLoop::doPendingFunctors()
{
    // 把vector<Functors> _pendings中的数据用tmp存起来，提高效率
    vector<Functors> tmp;
    {
        lock_guard<mutex> lg(_mutex);
        tmp.swap(_pendings);   // 交换后，_pendings中就被清空了
        /*
        swap函数知识点：
            如果你使用std::vector的 swap 函数交换两个 vector 对象，
            其中一个 vector 是空的，交换后原来非空的那个 vector 会被清空。
            这是因为 std::vector::swap 交换的是两个 vector 对象的内部资源，
            包括底层的数组和它们的大小等。如果其中一个 vector 是空的，
            那么它的底层数组为空，交换时它的内部状态将会被清空。
        */
    }

    for(auto &cb:tmp)
    {
        // 此处的“任务”就是线程池中的线程处理好之后的msg
        // 以及发送msg的连接TcpConnection的对象
        // 以及此TcpConnection对象发送数据能力的send函数，这三个所一起打包成的。
        cb(); // 执行“任务”
    }
}

// 参数cb就是需要存起来的“任务”
void EventLoop::runInLoop(Functors && cb)
{
    // 锁的粒度（范围）
    // 减小锁的范围(此处用块作用域，因为lock_guard锁出了当前作用域后会释放锁)
    // 线程池中的线程有多个，线程给vector中放完后就需要立即释放锁
    {
        lock_guard<mutex> lg(_mutex);
        _pendings.push_back(std::move(cb));
    }

    // 唤醒
    wakeup(); // 给内核计数器上写值，也就是会让用于通信的文件描述符_evtfd就绪
}